"""
配置变更通知器
提供配置变更事件的通知机制
"""

import asyncio
import logging
from abc import ABC, abstractmethod
from typing import List, Callable, Any


class ConfigNotifier(ABC):
    """配置变更通知器基类"""

    @abstractmethod
    async def on_config_changed(self, key: str, old_value: Any, new_value: Any):
        """配置变更回调"""
        pass

    @abstractmethod
    async def on_config_reloaded(self, key: str = None):
        """配置重载回调"""
        pass

    @abstractmethod
    async def on_service_restart(self):
        """服务重启回调"""
        pass


class LoggingNotifier(ConfigNotifier):
    """日志通知器 - 记录配置变更到日志"""

    def __init__(self, logger: logging.Logger = None):
        self.logger = logger or logging.getLogger(__name__)

    async def on_config_changed(self, key: str, old_value: Any, new_value: Any):
        """记录配置变更到日志"""
        self.logger.info(f"Configuration changed: {key} = '{old_value}' -> '{new_value}'")

    async def on_config_reloaded(self, key: str = None):
        """记录配置重载到日志"""
        if key:
            self.logger.info(f"Configuration reloaded: {key}")
        else:
            self.logger.info("All configurations reloaded")

    async def on_service_restart(self):
        """记录服务重启到日志"""
        self.logger.info("Service restart triggered by configuration change")


class CallbackNotifier(ConfigNotifier):
    """回调通知器 - 调用自定义回调函数"""

    def __init__(self):
        self.change_callbacks: List[Callable] = []
        self.reload_callbacks: List[Callable] = []
        self.restart_callbacks: List[Callable] = []

    def on_change(self, callback: Callable):
        """注册配置变更回调"""
        self.change_callbacks.append(callback)

    def on_reload(self, callback: Callable):
        """注册配置重载回调"""
        self.reload_callbacks.append(callback)

    def on_restart(self, callback: Callable):
        """注册服务重启回调"""
        self.restart_callbacks.append(callback)

    async def on_config_changed(self, key: str, old_value: Any, new_value: Any):
        """调用所有配置变更回调"""
        for callback in self.change_callbacks:
            try:
                if asyncio.iscoroutinefunction(callback):
                    await callback(key, old_value, new_value)
                else:
                    callback(key, old_value, new_value)
            except Exception as e:
                logging.error(f"Error in config change callback: {e}")

    async def on_config_reloaded(self, key: str = None):
        """调用所有配置重载回调"""
        for callback in self.reload_callbacks:
            try:
                if asyncio.iscoroutinefunction(callback):
                    await callback(key)
                else:
                    callback(key)
            except Exception as e:
                logging.error(f"Error in config reload callback: {e}")

    async def on_service_restart(self):
        """调用所有服务重启回调"""
        for callback in self.restart_callbacks:
            try:
                if asyncio.iscoroutinefunction(callback):
                    await callback()
                else:
                    callback()
            except Exception as e:
                logging.error(f"Error in service restart callback: {e}")


class WebhookNotifier(ConfigNotifier):
    """Webhook通知器 - 发送HTTP通知到外部系统"""

    def __init__(self, webhook_url: str, timeout: int = 10):
        self.webhook_url = webhook_url
        self.timeout = timeout
        self.logger = logging.getLogger(__name__)

    async def on_config_changed(self, key: str, old_value: Any, new_value: Any):
        """发送配置变更Webhook通知"""
        await self._send_webhook({
            'event_type': 'config_changed',
            'key': key,
            'old_value': old_value,
            'new_value': new_value,
            'timestamp': self._get_timestamp()
        })

    async def on_config_reloaded(self, key: str = None):
        """发送配置重载Webhook通知"""
        await self._send_webhook({
            'event_type': 'config_reloaded',
            'key': key,
            'timestamp': self._get_timestamp()
        })

    async def on_service_restart(self):
        """发送服务重启Webhook通知"""
        await self._send_webhook({
            'event_type': 'service_restart',
            'timestamp': self._get_timestamp()
        })

    async def _send_webhook(self, payload: dict):
        """发送Webhook请求"""
        try:
            import aiohttp

            async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=self.timeout)) as session:
                async with session.post(self.webhook_url, json=payload) as response:
                    if response.status == 200:
                        self.logger.info(f"Webhook sent successfully: {payload['event_type']}")
                    else:
                        self.logger.warning(f"Webhook failed with status {response.status}")
        except Exception as e:
            self.logger.error(f"Failed to send webhook: {e}")

    def _get_timestamp(self):
        """获取当前时间戳"""
        from datetime import datetime
        return datetime.now().isoformat()


class CompositeNotifier(ConfigNotifier):
    """组合通知器 - 将多个通知器组合使用"""

    def __init__(self, notifiers: List[ConfigNotifier] = None):
        self.notifiers = notifiers or []

    def add_notifier(self, notifier: ConfigNotifier):
        """添加通知器"""
        self.notifiers.append(notifier)

    def remove_notifier(self, notifier: ConfigNotifier):
        """移除通知器"""
        if notifier in self.notifiers:
            self.notifiers.remove(notifier)

    async def on_config_changed(self, key: str, old_value: Any, new_value: Any):
        """通知所有注册的通知器"""
        for notifier in self.notifiers:
            try:
                await notifier.on_config_changed(key, old_value, new_value)
            except Exception as e:
                logging.error(f"Error in notifier {type(notifier).__name__}: {e}")

    async def on_config_reloaded(self, key: str = None):
        """通知所有注册的通知器"""
        for notifier in self.notifiers:
            try:
                await notifier.on_config_reloaded(key)
            except Exception as e:
                logging.error(f"Error in notifier {type(notifier).__name__}: {e}")

    async def on_service_restart(self):
        """通知所有注册的通知器"""
        for notifier in self.notifiers:
            try:
                await notifier.on_service_restart()
            except Exception as e:
                logging.error(f"Error in notifier {type(notifier).__name__}: {e}")


class ModelReloadNotifier(ConfigNotifier):
    """模型重载通知器 - 当模型相关配置变更时通知模型管理器"""

    def __init__(self, model_manager=None):
        self.model_manager = model_manager
        self.logger = logging.getLogger(__name__)

    async def on_config_changed(self, key: str, old_value: Any, new_value: Any):
        """模型配置变更时重载相关模型"""
        if self._is_model_config(key):
            self.logger.info(f"Model configuration changed: {key}, scheduling model reload")
            await self._reload_model_if_needed(key)

    async def on_config_reloaded(self, key: str = None):
        """配置重载时重载相关模型"""
        if key and self._is_model_config(key):
            await self._reload_model_if_needed(key)

    async def on_service_restart(self):
        """服务重启时重载所有模型"""
        self.logger.info("Service restart, reloading all models")
        await self._reload_all_models()

    def _is_model_config(self, key: str) -> bool:
        """判断是否为模型相关配置"""
        model_keys = [
            'LLM_MODEL', 'ASR_MODEL', 'INDEX_TTS_MODEL',
            'RERANK_MODEL', 'EMBEDDING_MODEL',
            'ANY4DH_MODEL'
        ]
        return any(model_key in key for model_key in model_keys)

    async def _reload_model_if_needed(self, key: str):
        """根据配置键重载对应模型"""
        try:
            if self.model_manager:
                if 'LLM_MODEL' in key:
                    await self.model_manager.reload_llm_model()
                elif 'ASR_MODEL' in key:
                    await self.model_manager.reload_asr_model()
                elif 'INDEX_TTS_MODEL' in key:
                    await self.model_manager.reload_tts_model()
                elif 'RERANK_MODEL' in key:
                    await self.model_manager.reload_rerank_model()
                elif 'EMBEDDING_MODEL' in key:
                    await self.model_manager.reload_embedding_model()
                elif 'ANY4DH_MODEL' in key:
                    await self.model_manager.reload_digital_human_model()
        except Exception as e:
            self.logger.error(f"Failed to reload model for config {key}: {e}")

    async def _reload_all_models(self):
        """重载所有模型"""
        try:
            if self.model_manager:
                await self.model_manager.reload_all_models()
        except Exception as e:
            self.logger.error(f"Failed to reload all models: {e}")


class DatabaseConnectionNotifier(ConfigNotifier):
    """数据库连接通知器 - 数据库配置变更时重新连接"""

    def __init__(self, db_manager=None):
        self.db_manager = db_manager
        self.logger = logging.getLogger(__name__)

    async def on_config_changed(self, key: str, old_value: Any, new_value: Any):
        """数据库配置变更时重新连接"""
        if self._is_db_config(key):
            self.logger.info(f"Database configuration changed: {key}")
            await self._reconnect_database()

    async def on_config_reloaded(self, key: str = None):
        """配置重载时重新连接数据库"""
        if key and self._is_db_config(key):
            await self._reconnect_database()

    async def on_service_restart(self):
        """服务重启时重新连接数据库"""
        await self._reconnect_database()

    def _is_db_config(self, key: str) -> bool:
        """判断是否为数据库相关配置"""
        db_keys = ['MYSQL_', 'DB_', 'SQL_DB_']
        return any(key.startswith(db_key) for db_key in db_keys)

    async def _reconnect_database(self):
        """重新连接数据库"""
        try:
            if self.db_manager:
                await self.db_manager.close()
                await self.db_manager.initialize()
                self.logger.info("Database reconnected successfully")
        except Exception as e:
            self.logger.error(f"Failed to reconnect database: {e}")


class FileConfigNotifier(ConfigNotifier):
    """文件配置通知器 - 将配置变更同步到文件"""

    def __init__(self, config_file_path: str = '.env'):
        self.config_file_path = config_file_path
        self.logger = logging.getLogger(__name__)

    async def on_config_changed(self, key: str, old_value: Any, new_value: Any):
        """配置变更时同步到文件"""
        await self._sync_config_to_file(key, new_value)

    async def on_config_reloaded(self, key: str = None):
        """配置重载时同步到文件"""
        # 重载时通常不需要同步到文件
        pass

    async def on_service_restart(self):
        """服务重启时同步所有配置到文件"""
        await self._sync_all_configs_to_file()

    async def _sync_config_to_file(self, key: str, value: Any):
        """同步单个配置到文件"""
        try:
            import os
            from dotenv import set_key

            # 设置环境变量
            os.environ[key] = str(value)

            # 写入.env文件
            set_key(self.config_file_path, key, str(value))
            self.logger.debug(f"Synced config {key} to file")

        except Exception as e:
            self.logger.error(f"Failed to sync config {key} to file: {e}")

    async def _sync_all_configs_to_file(self):
        """同步所有配置到文件"""
        # 这个方法需要获取当前所有配置并写入文件
        # 具体实现取决于如何获取当前配置
        pass


# 通知器工厂
class NotifierFactory:
    """通知器工厂"""

    @staticmethod
    def create_logging_notifier(logger: logging.Logger = None) -> LoggingNotifier:
        """创建日志通知器"""
        return LoggingNotifier(logger)

    @staticmethod
    def create_callback_notifier() -> CallbackNotifier:
        """创建回调通知器"""
        return CallbackNotifier()

    @staticmethod
    def create_webhook_notifier(webhook_url: str, timeout: int = 10) -> WebhookNotifier:
        """创建Webhook通知器"""
        return WebhookNotifier(webhook_url, timeout)

    @staticmethod
    def create_composite_notifier(notifiers: List[ConfigNotifier] = None) -> CompositeNotifier:
        """创建组合通知器"""
        return CompositeNotifier(notifiers)

    @staticmethod
    def create_model_reload_notifier(model_manager=None) -> ModelReloadNotifier:
        """创建模型重载通知器"""
        return ModelReloadNotifier(model_manager)

    @staticmethod
    def create_database_connection_notifier(db_manager=None) -> DatabaseConnectionNotifier:
        """创建数据库连接通知器"""
        return DatabaseConnectionNotifier(db_manager)

    @staticmethod
    def create_file_config_notifier(config_file_path: str = '.env') -> FileConfigNotifier:
        """创建文件配置通知器"""
        return FileConfigNotifier(config_file_path)